package com.qingcloud.df.executor.engine.context;

import cn.hutool.core.io.FileUtil;
import com.qingcloud.base.exception.WamingException;
import com.qingcloud.df.executor.engine.ExecuteEngine;
import com.qingcloud.df.executor.enums.ComponentRunModeEnum;
import com.qingcloud.df.executor.launcher.*;
import com.qingcloud.df.sdk.component.AbstractComponent;
import com.qingcloud.df.sdk.component.config.SystemRuntimeInfo;
import com.qingcloud.df.sdk.component.context.CompInstContext;
import com.qingcloud.df.sdk.component.context.ConfigManager;
import com.qingcloud.df.sdk.component.context.ConfigService;
import com.qingcloud.df.sdk.component.enums.ComponentTypeEnum;
import com.qingcloud.df.sdk.component.extractor.ComponentContainer;
import com.qingcloud.df.sdk.component.utils.ComponentCommUtils;
import com.qingcloud.df.sdk.component.utils.ComponentInstUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.hutool.core.text.CharSequenceUtil.isBlank;
import static cn.hutool.core.text.CharSequenceUtil.isNotBlank;

/**
 * 组件执行引擎context
 *
 * @author wangjinnan
 */
@Service
public class ExecuteEngineContext {

    Logger logger = LoggerFactory.getLogger(getClass());
    @Resource
    private LocalTaskLauncher localTaskLauncher;
    @Resource
    private RemoteTaskLauncher remoteTaskLauncher;
    @Resource
    private ExecuteEngine executeEngine;
    @Resource
    private ConfigService downloadService;
    @Value("${qingcloud.executor.akka.node}")
    private String address;
    @Value("${qingcloud.executor.task.type}")
    private String taskType;
    @Value("${qingcloud.executor.componentDownload}")
    private boolean componentDownload;


    /**
     * 准备运行环境，创建实例对象
     *
     * @param instance 组件实例信息
     */

    private void check(CompInstContext instance) {
        //检查组件实例是否符合规范
        checkInstance(instance);
        //创建工作目录
        checkWorkDir(instance);
        //加载组件
        checkComponent(instance);
    }

    private CompInstContext initContext(Integer instanceId, String env){
        //通过配置文件查询组件信息
        CompInstContext context = ConfigManager.reloadInstance(instanceId, env);
        if (Objects.isNull(context)) {
            throw new WamingException("未找到的组件配置信息实例-["+instanceId+"]-["+env+"]");
        }
        check(context);
        return context;
    }

    /**
     * 启动组件实例
     *
     * @param instanceId 组件实例ID
     */
    public void startComponent(Integer instanceId, String env) throws Exception{
        CompInstContext context= initContext(instanceId, env);
        executeEngine.setLauncher(getLauncher(context));
        executeEngine.startComponent(context);
        updateInstanceSystemRunInfo(context,1);
    }

    /**
     * 按ID停止组件实例
     *
     * @param instanceId 组件实例ID
     */
    public void stopComponent(Integer instanceId, String env){
        try {
            CompInstContext context = ConfigManager.loadByInstanceId(instanceId, env);
            executeEngine.setLauncher(getLauncher(context));
            executeEngine.stopComponent(context);
            updateInstanceSystemRunInfo(context,0);
//            ConfigManager.remove(context.getId(), context.getEnv());
        } catch(Exception e){
            logger.error("stop instance-[{}] error ", instanceId);
            e.printStackTrace();
        }
    }

    /**
     * 按ID停止组件实例
     *
     * @param instanceId 组件实例ID
     */
    public void destroyComponent(Integer instanceId, String env) {
        try {
            CompInstContext compInstContext = ConfigManager.loadByInstanceId(instanceId, env);
            String instanceWorkspace = compInstContext.getInstWorkspacePath();
            this.stopComponent(instanceId, env);
            if (instanceWorkspace != null) {
                //删除实例配置文件
                if(FileUtil.exist(instanceWorkspace)) {
                    FileUtil.del(instanceWorkspace);
                }
                logger.debug("删除实例配置文件[{}]成功", instanceWorkspace);
            }
            ConfigManager.remove(compInstContext.getId(), compInstContext.getEnv());
        }
        catch(Exception e){
            logger.error("destroy instance-[{}] error ", instanceId);
            e.printStackTrace();
        }
    }

    private void updateInstanceSystemRunInfo(CompInstContext context, Integer status){
        SystemRuntimeInfo systemRuntimeInfo = new SystemRuntimeInfo();
        systemRuntimeInfo.setExecutorRestAddress(address);
        systemRuntimeInfo.setExecutorAkkaAddress(address);
        systemRuntimeInfo.setStatus(status);
        ComponentInstUtils.createRuntimeConfigFile(context.getInstWorkspacePath(), systemRuntimeInfo);
        context.setSystemRuntimeInfo(systemRuntimeInfo);
    }

    private Launcher getLauncher(CompInstContext context) {
        if(ComponentTypeEnum.COLLECTOR.code==context.getType()){  //嵌入式采集器(定时任务)
            if(taskType.equals("local")) {
                return localTaskLauncher;
            } else {
                return remoteTaskLauncher;
            }
        }
        if(context.getRunMode()==1 ) {//常驻进程    独立式
            return new DaemonLauncher();
        } else if(context.getRunMode()==2) { //消费类线程  嵌入式
            return new ConsumerLauncher();
        } else {
            throw new WamingException("未找到合适的执行引擎");
        }
    }

    /**
     * 检查组件实例是否符合规范
     *
     * @param instance 组件实例详情
     * @return 错误信息
     */
    private void checkInstance(CompInstContext instance) {
        //检查组件是否有可执行文件或者源码段
        //独立式运行的，必须要有自己的可执行文件
        if (ComponentRunModeEnum.ALONE.code.equals(instance.getRunMode())
                && isBlank(instance.getFilePath())) {
            throw new WamingException("ID是" + instance.getId() + "的组件，独立运行的文件路径是空的");
        }
    }

    /**
     * 创建组件例工工作目录
     *
     * @param instance 组件对象
     */
    private void checkWorkDir(CompInstContext instance) {
        //组件实例工作目录
        String instWorkspace = ComponentCommUtils.getCompInstWorkspace(instance.getJobId(), instance.getId(), instance.getSoftwareName(), instance.getEnv());
        //创建目录
        if (FileUtil.exist(instWorkspace)) {
            FileUtil.mkdir(instWorkspace);
        }
    }

    /**
     * 加载组件，java lib需要加载jar包，并实例化组件实现类
     *
     * @param instance 组件对象
     */
    private void checkComponent(CompInstContext instance) {

        //非独立执行的组件，直接加载导内存中，独立执行的组件需要拷贝组件导组件实例工作目录
        if (!ComponentRunModeEnum.ALONE.code.equals(instance.getRunMode())) {
            String componentNameEn = instance.getSoftwareName();
            String objectPath = instance.getFilePath();
            String instWorkspace = ComponentCommUtils.getCompInstWorkspace(instance.getJobId(), instance.getId(), instance.getSoftwareName(), instance.getEnv());
            //组件工作根目录目录
            String compWorkspace = ComponentCommUtils.getComponentWorkspace(instance.getComponentId(), instance.getSoftwareName())+File.separator+instance.getSoftwareName();
            //依赖包目录
            String libParent = compWorkspace + File.separator + "libs";
            //组件实际Jar路径
            String jarPath = compWorkspace + File.separator + componentNameEn + ".jar";
            File file = new File(jarPath);
            if (!file.exists() ) { //必须以组件英文名称为根目录
                if(componentDownload) {
                    downloadService.downloadComponent(compWorkspace, objectPath);
                }
            }
            if (!file.exists() ) {
                logger.error("can not find this component -[{}]--[{}],componentDownload status is {}. please contact administrator!", componentNameEn, jarPath,componentDownload);
                return;
            }
            //查询组件实例对象
            AbstractComponent component = ComponentContainer.get(instance.getEnv() + instance.getId(), AbstractComponent.class);
            if (Objects.isNull(component)) {
                //组件依赖Jar包
                List<String> dependencies = FileUtil.listFileNames(new File(libParent).getPath()).stream().map(it -> libParent + File.separator + it).collect(Collectors.toList());
                //加载文件中的组件实现类
                String result = ComponentContainer.loadLibInstance(instance.getEnv() + instance.getId(),
                        instance.getComponentId(), jarPath, dependencies, AbstractComponent.class);

                if (isNotBlank(result)) {
                    throw new WamingException("load lib " + jarPath + " fail: " + result);
                }
            }
        }
        //TODO check images
    }



}
